home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / xvisrc.zip / OS2VIO.C < prev    next >
C/C++ Source or Header  |  1992-07-28  |  18KB  |  857 lines

  1. #ifndef lint
  2. static char *sccsid = "@(#)os2vio.c    2.1 (Chris & John Downey) 7/29/92";
  3. #endif
  4.  
  5. /***
  6.  
  7. * program name:
  8.     xvi
  9. * function:
  10.     PD version of UNIX "vi" editor, with extensions.
  11. * module name:
  12.     os2vio.c
  13.  
  14. * module function:
  15.     OS/2 system interface module.
  16.  
  17.     This is a character-based implementation using the VIO & KBD
  18.     families of system calls. It doesn't use the Presentation Manager
  19.     but, on OS/2 version 1.* at least, it can be made to work in a PM
  20.     shell window by using markexe (see makefile.os2).
  21.  
  22.     Like the MS-DOS version, this one saves the screen contents &
  23.     restores them when it exits.
  24.  
  25.     Currently, the mouse input code doesn't work, & so is commented
  26.     out. I suspect that, if we want to have both mouse & keyboard
  27.     input, we have to use a device monitor, or develop a real PM
  28.     implementation.
  29. * history:
  30.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  31.     Originally by Tim Thompson (twitch!tjt)
  32.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  33.     Heavily modified by Chris & John Downey
  34.  
  35. ***/
  36.  
  37. #include "xvi.h"
  38.  
  39. #define NOMOUSE
  40.  
  41. #ifdef __ZTC__
  42. /*
  43.  * Set default stack size.
  44.  *
  45.  * See i286.asm for an explanation of why it has to be so big.
  46.  */
  47. unsigned    _stack = 44 * 1024;
  48. #endif
  49.  
  50. /*
  51.  * These are globals which are set by the system interface or terminal
  52.  * interface module, & used for various purposes throughout the rest
  53.  * of xvi.
  54.  *
  55.  * Number of rows & columns in the current window.
  56.  */
  57. unsigned    Rows,
  58.         Columns;
  59. /*
  60.  * Current position for screen writes.
  61.  */
  62. unsigned char    virt_row,
  63.         virt_col;
  64. /*
  65.  * Screen cell (character & attribute): current colour is stored here.
  66.  */
  67. unsigned char    curcell [2];
  68.  
  69. /*
  70.  * Time of last keypress or mouse button press (or garbage if
  71.  * (keystrokes < PSVKEYS)).
  72.  *
  73.  * This should only be referenced within a thread's critical section.
  74.  * Referencing a 32-bit variable is not generally an atomic operation
  75.  * on the 80286.
  76.  */
  77. static volatile clock_t lastevent;
  78.  
  79. #ifndef NOMOUSE
  80.     /*
  81.      * This is FALSE if we don't appear to have a mouse driver.
  82.      */
  83.     static bool_t    usemouse;
  84.  
  85.     /*
  86.      * Our logical mouse handle.
  87.      */
  88.     static HMOU    mousenum;
  89.  
  90. #else    /* NOMOUSE */
  91. #   define    usemouse    FALSE
  92. #endif    /* NOMOUSE */
  93.  
  94. #ifndef NOMOUSE
  95.     /*
  96.      * Hide mouse cursor.
  97.      */
  98.     static void
  99.     hidemouse()
  100.     {
  101.     NOPTRRECT    r;
  102.  
  103.     r.row = r.col = 0;
  104.     r.cRow = Rows - 1;
  105.     r.cCol = Columns - 1;
  106.     (void) MouRemovePtr((PNOPTRRECT) &r, mousenum);
  107.     }
  108.  
  109. #endif    /* NOMOUSE */
  110.  
  111. /*
  112.  * Show mouse cursor. (This is for symmetry with hidemouse().)
  113.  */
  114. #define showmouse()    ((void) MouDrawPtr(mousenum))
  115.  
  116. static long    semvec [2];
  117.  
  118. /*
  119.  * This semaphore needs to be acquired by a thread before it enters a
  120.  * critical region.
  121.  */
  122. #define        control ((HSEM)(long FAR *)&semvec[0])
  123.  
  124. /*
  125.  * This semaphore is used for communication between the main thread &
  126.  * the thread which handles automatic buffer preservation. It should
  127.  * be clear when (keystrokes >= PSVKEYS), otherwise it should be set.
  128.  */
  129. #define        psvsema ((HSEM)(long FAR *)&semvec[1])
  130.  
  131. #ifndef NOMOUSE
  132.  
  133. static void
  134. mousehandler()
  135. {
  136.     for (;;) {
  137.     MOUEVENTINFO    m;
  138.     unsigned short    status;
  139.     clock_t        start;
  140.  
  141. #if 0
  142.     if (MouGetDevStatus((PUSHORT) &status, mousenum) != 0
  143.         ||
  144.         (status & (MOUSE_UNSUPPORTED_MODE | MOUSE_DISABLED))
  145.     ) {
  146.         hidemouse();
  147.         (void) MouClose(mousenum);
  148.         DosExit(EXIT_THREAD, 0);
  149.     }
  150. #endif
  151.     status = MOU_WAIT;
  152.     MouReadEventQue((PMOUEVENTINFO) &m, (PUSHORT) &status, mousenum);
  153.     /*
  154.      * If we don't get the control semaphore immediately,
  155.      * we do nothing. Delayed responses to mouse button
  156.      * presses could be confusing.
  157.      */
  158. #if 0
  159.     start = clock();
  160. #endif
  161.     if (DosSemRequest(control, SEM_IMMEDIATE_RETURN) != 0)
  162.         continue;
  163. #if 0
  164.     if (clock() != start) {
  165.         (void) fprintf(stderr, "mouse thread: %d\n", __LINE__);
  166.         DosSemClear(control);
  167.         continue;
  168.     }
  169. #endif
  170.     /*
  171.      * Start of critical section.
  172.      */
  173.     if (++keystrokes >= PSVKEYS)
  174.         lastevent = clock();
  175.     if (State == NORMAL &&
  176.         (m.fs & (MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN))) {
  177.         hidemouse();
  178.         mouseclick(m.row, m.col);
  179.         showmouse();
  180.     }
  181.     /*
  182.      * End of critical section.
  183.      */
  184.     DosSemClear(control);
  185.     }
  186. }
  187.  
  188. #endif    /* NOMOUSE */
  189.  
  190. /*
  191.  * Macro to convert clock ticks to milliseconds.
  192.  */
  193. #if CLK_TCK == 1000
  194. #   define CLK2MS(c)        (c)
  195. #else
  196. #   if CLK_TCK < 1000
  197. #    define CLK2MS(c)    ((c) * (1000 / CLK_TCK))
  198. #   else
  199. #    define CLK2MS(c)    ((c) / (CLK_TCK / 1000))
  200. #   endif    /* CLK_TCK > 1000 */
  201. #endif    /* CLK_TCK != 1000 */
  202.  
  203. /*
  204.  * Number of keystrokes or mouse button presses since the last buffer
  205.  * preservation.
  206.  */
  207. volatile int        keystrokes;
  208.  
  209. /*
  210.  * This function handles automatic buffer preservation. It runs in its
  211.  * own thread, which is only awake when keystrokes >= PSVKEYS and the
  212.  * main thread is waiting for keyboard input. Even then, it spends
  213.  * most of its time asleep.
  214.  */
  215. static void FAR
  216. psvhandler()
  217. {
  218.     for (;;) {
  219.     long    sleeptime;
  220.  
  221.     DosSemWait(psvsema, SEM_INDEFINITE_WAIT);
  222.     DosSemRequest(control, SEM_INDEFINITE_WAIT);
  223.     /*
  224.      * Start of critical section.
  225.      */
  226.     if (keystrokes < PSVKEYS) {
  227.         sleeptime = 0;
  228.         /*
  229.          * If we haven't had at least PSVKEYS
  230.          * keystrokes, psvsema should be set.
  231.          */
  232.         DosSemSet(psvsema);
  233.     } else if ((sleeptime = (long) Pn(P_preservetime) * 1000 -
  234.               CLK2MS(clock() - lastevent)) <= 0) {
  235.         /*
  236.          * If Pn(P_presevetime) seconds haven't yet
  237.          * elapsed, sleep until they should have - but
  238.          * NOT within the critical section (!).
  239.          *
  240.          * Otherwise do automatic preserve.
  241.          *
  242.          * do_preserve() should reset keystrokes to 0.
  243.          */
  244.         (void) do_preserve();
  245.         sleeptime = 0;
  246.     }
  247.     /*
  248.      * End of critical section.
  249.      */
  250.     DosSemClear(control);
  251.     /*
  252.      * Sleep if we have to.
  253.      */
  254.     if (sleeptime != 0)
  255.         DosSleep(sleeptime);
  256.     }
  257. }
  258.  
  259. /*
  260.  * inchar() - get a character from the keyboard.
  261.  *
  262.  * Timeout not implemented yet for OS/2.
  263.  */
  264. int
  265. inchar(long mstimeout)
  266. {
  267.     for (;;) {
  268.     KBDKEYINFO k;
  269.     bool_t    mstatus,
  270.         psvstatus;
  271.  
  272.     flush_output();
  273.  
  274.     mstatus = (usemouse && State == NORMAL);
  275.     psvstatus = (keystrokes >= PSVKEYS);
  276.     /*
  277.      * We don't have to give control to any other thread
  278.      * if neither of these conditions is true.
  279.      */
  280.     if (mstatus || psvstatus) {
  281. #ifndef NOMOUSE
  282.         if (mstatus)
  283.         showmouse();
  284. #endif
  285.         if (psvstatus && DosSemWait(psvsema, SEM_IMMEDIATE_RETURN)
  286.                             == ERROR_SEM_TIMEOUT) {
  287.         /*
  288.          * If psvsema is set, clear it.
  289.          */
  290.         DosSemClear(psvsema);
  291.         }
  292.         DosSemClear(control);
  293.     }
  294.     /*
  295.      * Start of non-critical section.
  296.      *
  297.      * Wait for character from keyboard.
  298.      */
  299.     KbdCharIn((PKBDKEYINFO) &k, IO_WAIT, 0);
  300.     /*
  301.      * End of non-critical section.
  302.      */
  303.     if (mstatus || psvstatus) {
  304.         DosSemRequest(control, SEM_INDEFINITE_WAIT);
  305. #ifndef NOMOUSE
  306.         if (mstatus)
  307.         hidemouse();
  308. #endif
  309.     }
  310.     if (++keystrokes >= PSVKEYS)
  311.         lastevent = clock();
  312.     /*
  313.      * Now deal with the keypress information.
  314.      */
  315.     if ((unsigned char) k.chChar == (unsigned char) 0xe0) {
  316.     /*
  317.      * It's (probably) a function key.
  318.      */
  319.         if (k.chScan == 0x53)
  320.         /*
  321.          * It's the delete key.
  322.          */
  323.         return State == NORMAL ? 'x' : '\b';
  324.          /* else */
  325.         if (State == NORMAL) {
  326.         /*
  327.          * Assume it must be a function key.
  328.          */
  329.         switch (k.chScan) {
  330.             case 0x3b: return(K_HELP);
  331.             /* F1 key */
  332.             case 0x47: return('H');
  333.             /* home key */
  334.             case 0x48: return('k');
  335.             /* up arrow key */
  336.             case 0x49: return(CTRL('B'));
  337.             /* page up key */
  338.             case 0x4b: return('\b');
  339.             /* left arrow key */
  340.             case 0x4d: return(' ');
  341.             /* right arrow key */
  342.             case 0x4f: return('L');
  343.             /* end key */
  344.             case 0x50: return('j');
  345.             /* down arrow key */
  346.             case 0x51: return(CTRL('F'));
  347.             /* page down key */
  348.             case 0x52: return('i');
  349.             /* insert key */
  350.             default:
  351.             /* just ignore it ... */
  352.             continue;
  353.         }
  354.         /*
  355.          * If we aren't in command mode, 0xe0
  356.          * is a perfectly legitimate
  357.          * character, & we can't really tell
  358.          * whether or not it's supposed to be
  359.          * a function key, so we just have to
  360.          * return it as is.
  361.          */
  362.         }
  363.     }
  364.     return (unsigned char) k.chChar;
  365.     }
  366. }
  367.  
  368. void
  369. outchar(int c)
  370. {
  371.     curcell [0] = c;
  372.     VioWrtNCell((PBYTE) curcell, 1, virt_row, virt_col, 0);
  373.     if (++virt_col >= Columns) {
  374.     virt_col -= Columns;
  375.     if (++virt_row >= Rows)
  376.         virt_row = Rows - 1;
  377.     }
  378. }
  379.  
  380. void
  381. outstr(char* s)
  382. {
  383.     unsigned len = strlen(s);
  384.  
  385.     VioWrtCharStrAtt((PCH) s, len, virt_row, virt_col,
  386.                      (PBYTE) & curcell [1], 0);
  387.     if ((virt_col += len) >= Columns) {
  388.     virt_col -= Columns;
  389.     if (++virt_row >= Rows)
  390.         virt_row = Rows - 1;
  391.     }
  392. }
  393.  
  394. void
  395. erase_display()
  396. {
  397.     curcell[1] = Pn(P_colour);
  398.     curcell[0] = ' ';
  399.     VioWrtNCell((PBYTE) curcell, Rows * Columns, 0, 0, 0);
  400. }
  401.  
  402. void
  403. erase_line()
  404. {
  405.     curcell [0] = ' ';
  406.     VioWrtNCell((PBYTE) curcell, Columns - virt_col, virt_row, virt_col, 0);
  407. }
  408.  
  409. void
  410. scroll_down(unsigned start, unsigned end, unsigned nlines)
  411. {
  412.     curcell [0] = ' ';
  413.     VioScrollDn(start, 0, end, Columns - 1, nlines, (PBYTE) curcell, 0);
  414. }
  415.  
  416. void
  417. scroll_up(unsigned start, unsigned end, unsigned nlines)
  418. {
  419.     curcell [0] = ' ';
  420.     VioScrollUp(start, 0, end, Columns - 1, nlines, (PBYTE) curcell, 0);
  421. }
  422.  
  423. /*
  424.  * Attributes for colour systems
  425.  */
  426. #define BRIGHT    8    /* only available for foreground colours */
  427. #define BLACK    0
  428. #define BLUE    1
  429. #define GREEN    2
  430. #define CYAN    (BLUE | GREEN)
  431. #define RED    4
  432. #define BROWN    (RED | GREEN)
  433. #define YELLOW    (BRIGHT | BROWN)
  434. #define WHITE    (RED | GREEN | BLUE)
  435.  
  436. /*
  437.  * macro to set up foreground & background colours
  438.  */
  439. #define mkcolour(f,b)    ((unsigned char) (((b) << 4) | ((f) & 0xf)))
  440.  
  441. static char                *oldscreen;
  442. static unsigned short            scrsize;
  443. static enum { m_SYS = 0, m_VI = 1 }    curmode;
  444.  
  445. /*
  446.  * Save screen contents & set up video & keyboard states for editor.
  447.  */
  448. void
  449. sys_startv()
  450. {
  451.     if (curmode == m_VI)
  452.     return;
  453.     if (oldscreen != NULL) {
  454.     /*
  455.      * Save contents of screen so we can restore them
  456.      * afterwards.
  457.      */
  458.     VioReadCellStr((PCH) oldscreen, (PUSHORT) &scrsize, 0, 0, 0);
  459.     }
  460.     set_colour(Pn(P_colour));
  461.     /*
  462.      * Change keyboard status.
  463.      *
  464.      * We only do this when we've disabled keyboard interrupts.
  465.      */
  466.     {
  467.     KBDINFO        k;
  468.  
  469.     k.cb = sizeof k;
  470.     KbdGetStatus((PKBDINFO) &k, 0);
  471.     k.fsMask = (k.fsMask
  472.         /*
  473.          * turn these flags off:
  474.          */
  475.          & ~(KEYBOARD_ECHO_ON |
  476.          KEYBOARD_ASCII_MODE |
  477.          KEYBOARD_MODIFY_STATE |
  478.          KEYBOARD_MODIFY_INTERIM |
  479.          KEYBOARD_MODIFY_TURNAROUND |
  480.          KEYBOARD_2B_TURNAROUND |
  481.          KEYBOARD_SHIFT_REPORT))
  482.         /*
  483.          * turn these flags on:
  484.          */
  485.         | KEYBOARD_ECHO_OFF |
  486.           KEYBOARD_BINARY_MODE;
  487.     KbdSetStatus((PKBDINFO) &k, 0);
  488.     }
  489.     curmode = m_VI;
  490. }
  491.  
  492. void
  493. sys_init()
  494. {
  495.     {
  496.     VIOMODEINFO    v;
  497.  
  498.     /*
  499.      * Get information about display.
  500.      */
  501.     v.cb = sizeof v;
  502.     VioGetMode((PVIOMODEINFO) &v, 0);
  503.     Rows = v.row;
  504.     Columns = v.col;
  505.     scrsize = (Rows - 1) * Columns * 2;
  506.     if (v.color >= COLORS_16) {
  507.         /*
  508.          * Statically defined values are for mono systems:
  509.          * these are defaults for colour systems.
  510.          */
  511.         set_param(P_colour, mkcolour(BRIGHT | WHITE, BLUE));
  512.         set_param(P_statuscolour, mkcolour(YELLOW, BLACK));
  513.         set_param(P_roscolour, mkcolour(BRIGHT | RED, BLACK));
  514.         set_param(P_systemcolour, mkcolour(BRIGHT | CYAN, BLACK));
  515.     }
  516.     }
  517.     oldscreen = malloc(scrsize);
  518.     /*
  519.      * We have to acquire this semaphore before we start any other
  520.      * threads.
  521.      */
  522.     DosSemSet(control);
  523. #ifndef NOMOUSE
  524.     /*
  525.      * Open mouse device if we can.
  526.      */
  527.     if (MouOpen((PSZ) NULL, (PHMOU) &mousenum) == 0
  528. #if 0
  529.     && MouSynch(0) != 0
  530. #endif
  531.     ) {
  532.     TID        mousethread;
  533.     short        mask = MOUSE_BN1_DOWN |
  534.                    MOUSE_BN2_DOWN |
  535.                    MOUSE_BN3_DOWN;
  536.  
  537.     hidemouse();
  538. #if 0
  539.     MouSetEventMask((PUSHORT) &mask, mousenum);
  540. #endif
  541.     /*
  542.      * Create concurrent thread to handle mouse events.
  543.      *
  544.      * According to Microsoft, the ES register should be
  545.      * set to 0 first.
  546.      */
  547.     DosCreateThread((PFNTHREAD) mousehandler,
  548.         (es0(), (PTID) &mousethread), (PBYTE) newstack(32000));
  549.     usemouse = TRUE;
  550.     }
  551. #endif    /* NOMOUSE */
  552.     /*
  553.      * Initialize semaphore for automatic buffer preservation. It
  554.      * should only be clear if (keystrokes >= PSVKEYS).
  555.      */
  556.     DosSemSet(psvsema);
  557.     /*
  558.      * Create concurrent thread to do automatic preserves.
  559.      *
  560.      * According to Microsoft, the ES register should be set to 0 first.
  561.      */
  562.     {
  563.     TID psvthread;
  564.  
  565.     if (DosCreateThread((PFNTHREAD) psvhandler,
  566.             (es0(), (PTID) &psvthread),
  567.             (PBYTE) newstack(20000)) != 0) {
  568.         (void) fputs("Can't create thread for automatic preserves\r\n",
  569.              stderr);
  570.         exit(1);
  571.     }
  572.     }
  573.     /*
  574.      * Disable system critical error handler.
  575.      */
  576.     DosError(HARDERROR_DISABLE);
  577.     sys_startv();
  578. }
  579.  
  580. /*
  581.  * Restore video & keyboard states to what they were when we started.
  582.  *
  583.  * sys_endv() can be called when we're already in system mode, so we
  584.  * have to check.
  585.  */
  586. void
  587. sys_endv()
  588. {
  589.     KBDINFO k;
  590.  
  591.     if (curmode == m_SYS)
  592.     return;
  593.     k.cb = sizeof k;
  594.     KbdGetStatus((PKBDINFO) &k, 0);
  595.     k.fsMask = (k.fsMask
  596.         /*
  597.          * turn these flags off:
  598.          */
  599.          & ~(KEYBOARD_ECHO_OFF |
  600.          KEYBOARD_BINARY_MODE |
  601.          KEYBOARD_MODIFY_STATE |
  602.          KEYBOARD_MODIFY_INTERIM |
  603.          KEYBOARD_MODIFY_TURNAROUND |
  604.          KEYBOARD_2B_TURNAROUND |
  605.          KEYBOARD_SHIFT_REPORT))
  606.         /*
  607.          * turn these flags on:
  608.          */
  609.         | KEYBOARD_ECHO_ON |
  610.           KEYBOARD_ASCII_MODE;
  611.     KbdSetStatus((PKBDINFO) &k, 0);
  612.     if (oldscreen != (char*) 0)
  613.     /*
  614.      * Restore contents of screen saved by
  615.      * sys_startv().
  616.      */
  617.     VioWrtCellStr((PCH) oldscreen, scrsize, 0, 0, 0);
  618.     tty_goto(Rows - 1, 0);
  619.     set_colour(Pn(P_systemcolour));
  620.     erase_line();
  621.     flush_output();
  622.     curmode = m_SYS;
  623. }
  624.  
  625. void
  626. sys_exit(int r)
  627. {
  628.     sys_endv();
  629. #ifndef NOMOUSE
  630.     if (usemouse)
  631.     MouClose(mousenum);
  632. #endif
  633.     exit(r);
  634. }
  635.  
  636. void
  637. sleep(unsigned seconds)
  638. {
  639.     DosSleep(seconds * (long) 1000);
  640. }
  641.  
  642. /*
  643.  * This function is only used by tempfname(). It constructs a filename
  644.  * suffix based on an index number.
  645.  *
  646.  * The suffix ".$$$" is commonly used for temporary file names on
  647.  * MS-DOS & OS/2 systems. We also use the sequence ".$$1", ".$$2" ...
  648.  * ".fff" (all digits are hexadecimal).
  649.  */
  650. static char*
  651. hexsuffix(unsigned i)
  652. {
  653.     static char    suffix[] = ".$$$";
  654.     static char    hextab[] = "0123456789abcdef";
  655.     char    *sp = &suffix[3];
  656.  
  657.     while (sp > suffix) {
  658.     if (i > 0) {
  659.         *sp-- = hextab[i & 0xf];
  660.         i >>= 4;
  661.     } else {
  662.         *sp-- = '$';
  663.     }
  664.     }
  665.     return suffix;
  666. }
  667.  
  668. /*
  669.  * Construct unique name for temporary file, to be used as a backup
  670.  * file for the named file.
  671.  */
  672. char *
  673. tempfname(char *srcname)
  674. {
  675.     char    *srctail,
  676.         *srcdot,
  677.         *endp,
  678.         *retp;
  679.     unsigned    indexnum = 0;
  680.     unsigned    baselen;
  681.  
  682.     srctail = srcdot = NULL;
  683.     endp = srcname;
  684.  
  685.     while (*endp) {
  686.     switch (*endp++) {
  687.     case '\\':
  688.     case '/':
  689.         srctail = endp;
  690.         srcdot = (char*) 0;
  691.         continue;
  692.     case '.':
  693.         srcdot = endp - 1;
  694.     }
  695.     }
  696.     if (srctail == NULL) {
  697.     /*
  698.      * We haven't found any directory separators ('/' or '\\').
  699.      */
  700.     srctail = srcname;
  701.     /*
  702.      * Check to see if there's a disk drive name. If there
  703.      * is, skip over it.
  704.      */
  705.     if (*srcname && is_alpha(*srcname) && srcname[1] == ':')
  706.         srctail = &srcname[2];
  707.     }
  708.     /*
  709.      * There isn't a dot in the trailing part of the filename:
  710.      * just add it at the end.
  711.      */
  712.     if (srcdot == NULL)
  713.     srcdot = endp;
  714.     /*
  715.      * Don't make name too long.
  716.      */
  717.     if (srcdot - srctail > MAXNAMLEN - 4)
  718.     srcdot = srctail + MAXNAMLEN - 4;
  719.     if (srcdot - srcname > MAXPATHLEN - 4)
  720.     srcdot = srcname + MAXPATHLEN - 4;
  721.     baselen = srcdot - srcname;
  722.     /*
  723.      * Allocate space for new temporary file name ...
  724.      */
  725.     if ((retp = alloc(baselen + 5)) == (char*) 0)
  726.     return (char*) 0;
  727.     if (baselen > 0)
  728.     (void) memcpy(retp, srcname, baselen);
  729.     do {
  730.     /*
  731.      * Keep trying this until we get a unique file name.
  732.      */
  733.     strcpy(&retp[baselen], hexsuffix(indexnum++));
  734.     } while (exists(retp));
  735.     return retp;
  736. }
  737.  
  738. /*
  739.  * Fake out a pipe by writing output to temp file, running a process with
  740.  * i/o redirected from this file to another temp file, and then reading
  741.  * the second temp file back in.
  742.  *
  743.  * OS/2 does have real pipes, but I don't know how to avoid deadlock
  744.  * when connecting concurrent processes with bidirectional pipes.
  745.  */
  746. bool_t
  747. sys_pipe(cmd, writefunc, readfunc)
  748. char    *cmd;
  749. int    (*writefunc) P((FILE *));
  750. long    (*readfunc) P((FILE *));
  751. {
  752.     char    *temp1;
  753.     FILE    *fp;
  754.     bool_t    retval;
  755.  
  756.     /*
  757.      * Create first temporary file ...
  758.      */
  759.     if (
  760.     (temp1 = tempfname("xvi_out")) == NULL
  761.     ||
  762.     (fp = fopen(temp1, "w")) == NULL
  763.     ) {
  764.     retval = FALSE;
  765.     } else {
  766.     char    *temp2 = NULL;
  767.     int    savcon;
  768.     int    fd1 = -1,
  769.         fd2 = -1;
  770.  
  771.     /*
  772.      * ... then write to it & close it ...
  773.      */
  774.     (void) (*writefunc)(fp);
  775.     (void) fclose(fp);
  776.  
  777.     /*
  778.      * ... then re-open it for reading, open second one
  779.      * for writing & re-arrange file descriptors.
  780.      *
  781.      * Note that we assume that the editor's standard
  782.      * input, output & error files are the same device,
  783.      * since I can't imagine how any of them could
  784.      * usefully be redirected to anything else.
  785.      */
  786.  
  787. #ifndef O_BINARY
  788. #    define O_BINARY 0
  789. #endif
  790.     if (
  791.         (savcon = dup(0)) < 3
  792.         ||
  793.         (fd1 = open(temp1, O_RDONLY | O_BINARY)) < 3
  794.         ||
  795.         (temp2 = tempfname("xvi_in")) == NULL 
  796.         ||
  797.         (fd2 = open(temp2,
  798.                 O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600)) < 3
  799.     ) {
  800.         retval = FALSE;
  801.     } else {
  802.         (void) dup2(fd1, 0);
  803.         (void) dup2(fd2, 1);
  804.         (void) dup2(fd2, 2);
  805.  
  806.         (void) close(fd1);
  807.         (void) close(fd2);
  808.         fd1 = fd2 = -1;
  809.  
  810.         /*
  811.          * Run the command.
  812.          */
  813.         (void) system(cmd);
  814.  
  815.         /*
  816.          * Restore our standard input, output & error
  817.          * files.
  818.          */
  819.         (void) dup2(savcon, 0);
  820.         (void) dup2(savcon, 1);
  821.         (void) dup2(savcon, 2);
  822.  
  823.         /*
  824.          * Now read from the second temporary file,
  825.          * close it, & we're done.
  826.          */
  827.         if ((fp = fopen(temp2, "r")) == NULL) {
  828.         retval = FALSE;
  829.         } else {
  830.         (void) (*readfunc)(fp);
  831.         (void) fclose(fp);
  832.         retval = TRUE;
  833.         }
  834.     }
  835.     /*
  836.      * Clean up.
  837.      */
  838.     if (temp2) {
  839.         (void) remove(temp2);
  840.         free(temp2);
  841.     }
  842.     if (savcon > 2)
  843.         (void) close(savcon);
  844.     if (fd1 > 2)
  845.         (void) close(fd1);
  846.     if (fd2 > 2)
  847.         (void) close(fd2);
  848.     }
  849.  
  850.     if (temp1) {
  851.     (void) remove(temp1);
  852.     free(temp1);
  853.     }
  854.  
  855.     return(retval);
  856. }
  857.